package de.edb.dao;

import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import de.edb.dto.EventArtistDTO;
import de.edb.model.Artist;
import de.edb.model.Event;
import de.edb.model.Location;


public class EventDao extends HibernateDaoSupport {	
	SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy HH:mm");


	/**
	 * <p>returns all Event objects as EventArtistDTO objects.</p>
	 * @return A sorted set of EventArtistDTO objects
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Set<EventArtistDTO> findAllDTO() {
		HibernateTemplate template = getHibernateTemplate();
		return (Set<EventArtistDTO>) template.execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
				Criteria criteria = session.createCriteria(Event.class);
				List<Event> events = criteria.list();
				return createDtoSet(events);
			}
		});
	}
	
	
	/**
	 * <p>returns all future Event objects as EventArtistDTO objects.</p>
	 * @return A sorted set of EventArtistDTO objects
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Set<EventArtistDTO> findFutureDTO() {
		HibernateTemplate template = getHibernateTemplate();
		return (Set<EventArtistDTO>) template.execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
				Date now = new Date();
				Criteria criteria = session.createCriteria(Event.class);
				//criteria.add(Restrictions.isNull("end"));
				criteria.add(Restrictions.ge("begin", now));
				criteria.addOrder(Order.asc("begin"));
				List<Event> events = criteria.list();
				return createDtoSet(events);
			}
		});
	}
	

	/**
	 * <p>returns a limited number of future Event objects as EventArtistDTO objects.</p>
	 * @param limit
	 * @return A sorted set of EventArtistDTO objects
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Set<EventArtistDTO> findFutureDTO(final int limit) {
		HibernateTemplate template = getHibernateTemplate();
		return (Set<EventArtistDTO>) template.execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
				Date now = new Date();
				Criteria criteria = session.createCriteria(Event.class);
				//criteria.add(Restrictions.isNull("end"));
				criteria.add(Restrictions.ge("begin", now));
				criteria.addOrder(Order.asc("begin"));
				criteria.setMaxResults(limit);
				List<Event> events = criteria.list();
				return createDtoSet(events);
			}
		});
	}
	
	/**
	 * <p>returns all past Event objects as EventArtistDTO objects.</p>
	 * @return A sorted set of EventArtistDTO objects
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Set<EventArtistDTO> findPastDTO() {
		HibernateTemplate template = getHibernateTemplate();
		return (Set<EventArtistDTO>) template.execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
				Date now = new Date();
				Criteria criteria = session.createCriteria(Event.class);
				//criteria.add(Restrictions.isNull("end"));
				criteria.add(Restrictions.lt("begin", now));
				List<Event> events = criteria.list();
				return createDtoSet(events);
			}
		});
	}

	/**
	 * Private utility function
	 * @param events a List of persistent Event objects
	 * @return a Set of EventArtistDTO objects
	 */
	private Set<EventArtistDTO> createDtoSet(List<Event> events) {
		Set<EventArtistDTO> results = new TreeSet<EventArtistDTO>();
		for (Event e : events) {
			Date a = e.getEnd();
			results.add(new EventArtistDTO(e.getBegin(), 
					e.getId(),
					e.getName(),
					e.getLocation().getName(),
					sdf.format(e.getBegin()),
					(a == null ? "" : sdf.format(a)),
					artistString(e.getArtists())));
		}
		return results;
	}
	
	
	/**
	 * Private utility function
	 * @param events a List of persistent Event objects
	 * @return a Set of EventArtistDTO objects
	 */
	@SuppressWarnings("unused")
	private EventArtistDTO createDto(Event event) {
		EventArtistDTO result;

			Date a = event.getEnd();
			result = new EventArtistDTO(event.getBegin(), 
					event.getId(),
					event.getName(),
					event.getLocation().getName(),
					sdf.format(event.getBegin()),
					(a == null ? "" : sdf.format(a)),
					artistString(event.getArtists()));

		return result;
	}


	/**
	 * @param artistSet
	 * @return a String of the names of those Artist objects
	 */
	private String artistString(Set<Artist> artistSet) {
		String artists = "";
		String trenner = "";
		for (Artist a : artistSet) {
			artists = artists + trenner + a.getName();
			trenner = ", ";
		}
		return artists;
	}

	/**
	 * @return all Event objects in the database.
	 */
	@SuppressWarnings("unchecked")
	public List<Event> findAll() {
		HibernateTemplate template = getHibernateTemplate();
		@SuppressWarnings("rawtypes")
		List allEvents = template.loadAll(Event.class);
		return (List<Event>) allEvents;
	}


	/**
	 * Creates an Event
	 * @param locationID
	 * @param slot
	 * @param name
	 * @param description
	 * @param begin
	 * @param end
	 * @param price
	 * @param ticketlink
	 * @throws DaoException
	 */
	public void createEvent(int locationID, Integer[] slot, String name, String description, Date begin, Date end, Double price, String ticketlink) throws DaoException {
		HibernateTemplate template = getHibernateTemplate();
		Location l = (Location) template.get(Location.class, locationID);
		
		Set<Artist> a = new HashSet<Artist>();
		for(Integer artistId : slot) {
			a.add((Artist) template.get(Artist.class, artistId));
		}
		
		Event f = new Event(name, description, begin, end);
		
		f.setLocation(l);
		f.setArtists(a);
		f.setPrice(price);
		f.setTicketlink(ticketlink);
		if (f.getCreated() == null) f.setCreated(new Date());
		
		try {
			template.saveOrUpdate(f);
		} catch (DataAccessException e) {
			throw new DaoException("Ein Künstler kann nicht mehrmals am selben Event teilnehmen");
		}
	}


	/**
	 * Searches for all Events with a specific Location
	 * @param location
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Set<Event> findByLocation(Location location) {
		Session hibernateSession = getSession();
		Criteria crit = hibernateSession.createCriteria(Event.class);
		crit.add(Restrictions.eq("location", location));
		List<Event> tmp = crit.list();
		Set<Event> result = new HashSet<Event>();
		for (Event e : tmp) {
			result.add(e);
		}
		return result;
	}
	
	
	/**
	 * Searches for all Events with an specific Artist
	 * @param artist
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Set<Event> findByArtist(Artist artist) {
		Session hibernateSession = getSession();
		Criteria criteria = hibernateSession.createCriteria(Event.class);
		List<Event> allEvents = criteria.list();
		Set<Event> result = new HashSet<Event>();
		for (Event e : allEvents) {
			Set<Artist> artists = e.getArtists();
			for (Artist a : artists) {
				if(a.getId().equals(artist.getId())) result.add(e);
			}
		}
		return result;
	}
	
	
	/**
	 * @return Event objects filtered by name from the database.
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Set<EventArtistDTO> findByName(final String keyword) {
		HibernateTemplate template = getHibernateTemplate();
		return (Set<EventArtistDTO>) template.execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Criteria criteria = session.createCriteria(Event.class);
				criteria.add(Restrictions.ilike("name", keyword, MatchMode.ANYWHERE));
				List<Event> events = criteria.list();
				return createDtoSet(events);
			}
		});
		
	}
	
	
	
	/**
	 * Searches for an Event by its primary db key
	 * @param id
	 * @return
	 */
	public Event findById(int id) {	
		HibernateTemplate template = getHibernateTemplate();
		return (Event) template.get(Event.class, id);
	}
	

	/**
	 * Deletes an Event
	 * @param event
	 */
	public void delete(Event event) {
		HibernateTemplate template = getHibernateTemplate();
		template.delete(event);
	}
	
}